package org.xiaodongwei.jvm.thread;

import org.xiaodongwei.config.HotSpotConfig;
import org.xiaodongwei.jvm.exe.JVMPcRegister;
import org.xiaodongwei.jvm.exe.JVMStack;
import org.xiaodongwei.jvm.exe.JVMStackFrame;
import org.xiaodongwei.jvm.load.BootstrapClassLoader;
import org.xiaodongwei.jvm.op.*;

import tech.medivh.classpy.classfile.ClassFile;
import tech.medivh.classpy.classfile.MethodInfo;
import tech.medivh.classpy.classfile.bytecode.*;
import tech.medivh.classpy.classfile.constant.ConstantMethodrefInfo;
import tech.medivh.classpy.classfile.constant.ConstantPool;
import tech.medivh.classpy.classfile.jvm.Opcode;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;

/**
 * JVM线程，对JVM栈的管理 (这里是整个jvm虚拟机实现的核心对象，这里会封装整个jvm指令调用的过程)
 */
@SuppressWarnings("all")
public class JVMThread {
    private static final String TAG = "JVMThread";

    private JVMStack jvmStack;                              // JVM栈，管理整个过程中的栈帧

    private String jvmThreadName;                           // JVM线程名

    private BootstrapClassLoader classLoader;               // 类加载器，这里之所以需要是因为在执行的过程中需要动态加载其他类

    private JVMPcRegister jvmPcRegister;                    // JVM指令寄存器，用于对函数调用过程中jvm指令的获取，本质上是一个迭代器，用于获取一下个可执行的jvm指令

    private HashMap<Opcode, IOpCodeExecute> opCodeExecute;  // opcode执行器

    public JVMThread(String threadName, JVMStackFrame stackFrame, BootstrapClassLoader classLoader) {
        this.jvmThreadName = threadName;
        this.jvmStack = new JVMStack();
        this.jvmStack.push(stackFrame);
        this.classLoader = classLoader;
        this.jvmPcRegister = new JVMPcRegister(jvmStack);
        this.opCodeExecute = new HashMap<>();
    }

    /**
     * JVM执行线程的入口，其核心逻辑：通过寄存器获取下一条指令，然后按照Java虚拟机规范，执行即可
     * JVM虚拟机规范官方文档链接：https://docs.oracle.com/javase/specs/jvms/se23/html/index.html
     */
    public void start() throws Exception {
        /**
         * 当执行到_return 指令，说明当前调用结束了，需要及时将栈帧从jvm栈中移除，否则会出现空指针异常
         */
        for (Instruction instruction : jvmPcRegister) {
            if (HotSpotConfig.ENABLE_LOG) {
                System.out.println(instruction);
            }
            // System.out.println(instruction);
            // 当前栈帧的常量池, 注意：这里的常量池必须在for循环内部，因为在执行的过程中，当前栈顶的栈帧可能会变化，比如对应的_return指令会出栈
            // 当前函数内部存在其他函数调用，这时候会创建新的栈帧进行入栈，两种情况都会导致当前栈顶对应的栈帧发生变化， 从而对应的常量池也发生了变
            // 化
            ConstantPool constantPool = jvmStack.peek().getConstantPool();

            Opcode opcode = instruction.getOpcode();

            switch (opcode) {
                case getstatic -> {
                    if (opCodeExecute.containsKey(opcode)) {
                        opCodeExecute.get(opcode).execute(instruction);
                    } else {
                        GetStaticOpCodeExecute getStaticOpCodeExecute = new GetStaticOpCodeExecute(jvmStack, constantPool);
                        opCodeExecute.put(opcode, getStaticOpCodeExecute);
                        getStaticOpCodeExecute.execute(instruction);
                    }
                }
                case iconst_0, iconst_1, iconst_2, iconst_3, iconst_4, iconst_5 -> {
                    if (opCodeExecute.containsKey(opcode)) {
                        opCodeExecute.get(instruction.getOpcode()).execute(instruction);
                    } else {
                        IConstOpCodeExecute constOpCodeExecute = new IConstOpCodeExecute(opcode, jvmStack);
                        opCodeExecute.put(opcode, constOpCodeExecute);
                        constOpCodeExecute.execute(instruction);
                    }
                }
                case fconst_0, fconst_1, fconst_2 -> {
                    if (opCodeExecute.containsKey(opcode)) {
                        opCodeExecute.get(instruction.getOpcode()).execute(instruction);
                    } else {
                        FConstOpCodeExecute fConstOpCodeExecute = new FConstOpCodeExecute(opcode, jvmStack, constantPool);
                        opCodeExecute.put(opcode, fConstOpCodeExecute);
                        fConstOpCodeExecute.execute(instruction);
                    }
                }
                case bipush -> {
                    // TODO: 目前这里仅支持常量池范围的push，超过这个返回会会抛出异常，需要实现其他jvm指令
                    //       33: sipush 128: The current instruction has not been implemented yet
                    //       超过jvm常量池范围(-128-127)使用的是sipush这个jvm指令来进行push到局部变量表中
                    if (opCodeExecute.containsKey(opcode)) {
                        opCodeExecute.get(instruction.getOpcode()).execute(instruction);
                    } else {
                        BiPushOpCodeExecute biPushOpCodeExecute = new BiPushOpCodeExecute(jvmStack, constantPool);
                        opCodeExecute.put(opcode, biPushOpCodeExecute);
                        biPushOpCodeExecute.execute(instruction);
                    }
                }
                case sipush -> {
                    // 超过常量池范围之后[-128, 127]之后需要通过sipush指令进行推送
                    if (opCodeExecute.containsKey(opcode)) {
                        opCodeExecute.get(instruction.getOpcode()).execute(instruction);
                    } else {
                        SiPushOpcodeExecute siPushOpcodeExecute = new SiPushOpcodeExecute(jvmStack, constantPool);
                        opCodeExecute.put(opcode, siPushOpcodeExecute);
                        siPushOpcodeExecute.execute(instruction);
                    }
                }
                case iload_0, iload_1, iload_2, iload_3 -> {
                    if (opCodeExecute.containsKey(opcode)) {
                        opCodeExecute.get(instruction.getOpcode()).execute(instruction);
                    } else {
                        ILoadOpCodeExecute iLoadOpCodeExecute = new ILoadOpCodeExecute(opcode, jvmStack, constantPool);
                        opCodeExecute.put(opcode, iLoadOpCodeExecute);
                        iLoadOpCodeExecute.execute(instruction);
                    }
                }
                case fload_0, fload_1, fload_2, fload_3 -> {
                    // 从操作数栈中pop处float类型的元素
                    if (opCodeExecute.containsKey(opcode)) {
                        opCodeExecute.get(opcode).execute(instruction);
                    } else {
                        FLoadOpCodeExecute fLoadOpCodeExecute = new FLoadOpCodeExecute(opcode, jvmStack, constantPool);
                        opCodeExecute.put(opcode, fLoadOpCodeExecute);
                        fLoadOpCodeExecute.execute(instruction);
                    }
                }
                case ldc -> {
                    if (opCodeExecute.containsKey(opcode)) {
                        opCodeExecute.get(opcode).execute(instruction);
                    } else {
                        LdcOpcodeExecute ldcOpcodeExecute = new LdcOpcodeExecute(jvmStack, constantPool);
                        opCodeExecute.put(opcode, ldcOpcodeExecute);
                        ldcOpcodeExecute.execute(instruction);
                    }
                }
                case invokevirtual -> {
                    // 调用一个虚拟方法，JVM规范：从操作数栈中拿到对应的元素，然后执行这个虚拟方法即可（注：虚拟方法，即可被重写的方法）
                    // 创建这个函数调用的栈帧，然后入栈即可，等待当前调用栈帧在jvm栈的栈顶，从而执行（不是立即执行的，有一个入栈出栈的过程）
                    InvokeVirtual invokeVirtual = (InvokeVirtual) instruction;
                    // 这里的方法常量信息用于后面创建栈帧（还需要结合方法参数）
                    ConstantMethodrefInfo methodInfo = invokeVirtual.getMethodInfo(constantPool);
                    // 获取这个方法的类名, 方法名, 方法参数类型
                    String className = methodInfo.className(constantPool);
                    String methodName = methodInfo.methodName(constantPool);
                    List<String> params = methodInfo.paramClassName(constantPool);
                    if (className.contains("java")) {
                        Class<?> aClass = Class.forName(className);
                        Method declaredMethod = aClass.getDeclaredMethod(methodName,
                                params.stream().map(this::nameToClass).toArray(Class[]::new));
                        Object[] args = new Object[params.size()];
                        for (int index = args.length - 1; index >= 0; index--) {
                            args[index] = jvmStack.peek().getOperateStack().pop();
                        }
                        Object result = declaredMethod.invoke(jvmStack.peek().getOperateStack().pop(), args);
                        if (!methodInfo.isVoid(constantPool)) {
                            jvmStack.peek().pushToOperateStack(result);
                        }
                        break;
                    }
                    ClassFile classFile = classLoader.loadClass(className);
                    MethodInfo finalMethodInfo = classFile.getMethods(methodName).get(0);
                    Object[] args = new Object[params.size() + 1];
                    for (int index = args.length - 1; index >= 0; index--) {
                        args[index] = jvmStack.peek().getOperateStack().pop();
                    }
                    JVMStackFrame stackFrame = new JVMStackFrame(finalMethodInfo, classFile.getConstantPool(), args);
                    jvmStack.push(stackFrame);
                }
                case invokestatic -> {
                    // 执行静态方法
                    if (opCodeExecute.containsKey(opcode)) {
                        opCodeExecute.get(instruction.getOpcode()).execute(instruction);
                    } else {
                        InvokeStaticOpCodeExecute invokeStaticOpCodeExecute
                                = new InvokeStaticOpCodeExecute(jvmStack, constantPool, classLoader);
                        opCodeExecute.put(opcode, invokeStaticOpCodeExecute);
                        invokeStaticOpCodeExecute.execute(instruction);
                    }
                }
                case ifle -> {
                    if (opCodeExecute.containsKey(opcode)) {
                        opCodeExecute.get(opcode).execute(instruction);
                    } else {
                        IfleOpCodeExecute ifleOpCodeExecute = new IfleOpCodeExecute(jvmStack, constantPool);
                        opCodeExecute.put(opcode, ifleOpCodeExecute);
                        ifleOpCodeExecute.execute(instruction);
                    }
                }
                case if_icmple -> {
                    // 比较栈顶两个int值，如果第一个值 ≤ 第二个值，则跳转
                    if (opCodeExecute.containsKey(opcode)) {
                        opCodeExecute.get(opcode).execute(instruction);
                    } else {
                        IfIcmpleOpCodeExecute ifIcmpleOpCodeExecute = new IfIcmpleOpCodeExecute(jvmStack, constantPool);
                        opCodeExecute.put(opcode, ifIcmpleOpCodeExecute);
                        ifIcmpleOpCodeExecute.execute(instruction);
                    }
                }
                case fcmpl -> {
                    // 浮点数的比较，指令用于比较两个浮点数（float或double类型），并根据比较结果生成一个浮点数
                    if (opCodeExecute.containsKey(opcode)) {
                        opCodeExecute.get(opcode).execute(instruction);
                    } else {
                        FCmplOpCodeExecute fCmpOpCodeExecute = new FCmplOpCodeExecute(jvmStack, constantPool);
                        opCodeExecute.put(opcode, fCmpOpCodeExecute);
                        fCmpOpCodeExecute.execute(instruction);
                    }
                }
                case ireturn -> {
                    // 返回int类型的指令
                    if (opCodeExecute.containsKey(opcode)) {
                        opCodeExecute.get(opcode).execute(instruction);
                    } else {
                        IReturnOpCodeExecute iReturnOpCodeExecute= new IReturnOpCodeExecute(jvmStack, constantPool);
                        opCodeExecute.put(opcode, iReturnOpCodeExecute);
                        iReturnOpCodeExecute.execute(instruction);
                    }
                }
                case freturn -> {
                    // 返回一个float类型
                    // this.jvmStack.pop();
                    if (opCodeExecute.containsKey(opcode)) {
                        opCodeExecute.get(opcode).execute(instruction);
                    } else {
                        FReturnOpCodeExecute fReturnOpCodeExecute= new FReturnOpCodeExecute(jvmStack, constantPool);
                        opCodeExecute.put(opcode, fReturnOpCodeExecute);
                        fReturnOpCodeExecute.execute(instruction);
                    }
                }
                case _return -> {
                    // 说明当前栈帧执行完，可以出栈
                    this.jvmStack.pop();
                }

                default -> {
                    throw new IllegalArgumentException(instruction + ": The current instruction has not been implemented yet");
                }
            }
        }
    }

    private Class<?> nameToClass(String className) {
        if (className.equals("int")) {
            return int.class;
        } else if (className.equals("float")) {
            return float.class;
        }
        try {
            return Class.forName(className);
        } catch (Exception e) {
            return null;
        }
    }
}
